import tkinter as tk
import os
import pandas as pd
from sklearn.neighbors import KNeighborsClassifier
import numpy as np
import csv
from PIL import Image, ImageTk
import logging
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg

logging.basicConfig(level=logging.INFO, format='%(asctime)s:%(levelname)s:%(message)s')

class MovieRatingApp:
    def __init__(self, root):
        self.root = root
        root.title("Movie Rating App")
        root.geometry("900x600")

        # 加载并显示电影图片
        self.image1 = Image.open(os.path.join(os.path.dirname(__file__), 'post1_relie.jpg'))
        self.image1.thumbnail((100, 300))
        self.movie1_image = ImageTk.PhotoImage(self.image1)
        self.movie1_label = tk.Label(root, image=self.movie1_image)
        self.movie1_label.grid(row=0, column=0, padx=10, pady=10)

        self.image2 = Image.open(os.path.join(os.path.dirname(__file__), "post2_family.jpg"))
        self.image2.thumbnail((100, 300))
        self.movie2_image = ImageTk.PhotoImage(self.image2)
        self.movie2_label = tk.Label(root, image=self.movie2_image)
        self.movie2_label.grid(row=0, column=1, padx=10, pady=10)

        # 创建评分下拉菜单
        self.movie1_rating = tk.StringVar(root, value="1")
        tk.OptionMenu(root, self.movie1_rating, "1", "2", "3", "4", "5").grid(row=1, column=0, padx=10, pady=10)

        self.movie2_rating = tk.StringVar(root, value="1")
        tk.OptionMenu(root, self.movie2_rating, "1", "2", "3", "4", "5").grid(row=1, column=1, padx=10, pady=10)

        # 创建电影类型选择的单选按钮
        self.preference = tk.IntVar()
        tk.Radiobutton(root, text="动作片", variable=self.preference, value=0).grid(row=2, column=0, padx=10, pady=10)
        tk.Radiobutton(root, text="喜剧片", variable=self.preference, value=1).grid(row=2, column=1, padx=10, pady=10)

        # 创建按钮
        tk.Button(root, text="Confirm", command=self.confirm).grid(row=3, column=0, padx=10, pady=20)
        tk.Button(root, text="Clear", command=self.clear_plot).grid(row=3, column=1, padx=10, pady=20)
        tk.Button(root, text="Predict", command=self.predict_preference).grid(row=4, columnspan=2, padx=10, pady=20)
        self.predict_label = tk.Label(root, text="")
        self.predict_label.grid(row=5, columnspan=2, padx=10, pady=10)

        self.filename = os.path.join(os.path.dirname(__file__), 'ratings.csv')

        if not os.path.isfile(self.filename):
            with open(self.filename, 'w', newline='') as file:
                writer = csv.writer(file)
                writer.writerow(["Movie1", "Movie2", "Preference"])

        # 创建显示图像的框架
        self.plot_frame = tk.Frame(root)
        self.plot_frame.grid(row=0, column=2, rowspan=4, padx=10, pady=10)
        self.plot_ratings()

    def confirm(self):
        m1_rating = self.movie1_rating.get()
        m2_rating = self.movie2_rating.get()
        preference = self.preference.get()

        with open(self.filename, 'a', newline='') as file:
            writer = csv.writer(file)
            writer.writerow([m1_rating, m2_rating, preference])

        self.plot_ratings()

    def clear_plot(self):
        with open(self.filename, 'w', newline='') as file:
            writer = csv.writer(file)
            writer.writerow(["Movie1", "Movie2", "Preference"])
        self.predict_label.config(text="")
        self.plot_ratings()

    def plot_ratings(self):
        self.plot_frame.destroy()
        self.plot_frame = tk.Frame(self.root)
        self.plot_frame.grid(row=0, column=2, rowspan=4, padx=10, pady=10)

        df = pd.read_csv(self.filename)
        action_movies = df[df['Preference'] == 0]
        comedy_movies = df[df['Preference'] == 1]

        fig, ax = plt.subplots()
        ax.grid(True)
        ax.scatter(action_movies['Movie1'], action_movies['Movie2'], color='orange', label='Action')
        ax.scatter(comedy_movies['Movie1'], comedy_movies['Movie2'], color='blue', label='Comedy')

        # 把电影的坐标添加到图像上
        for i in range(len(action_movies)):
            ax.text(action_movies['Movie1'].iloc[i], action_movies['Movie2'].iloc[i], f'({action_movies["Movie1"].iloc[i]}, {action_movies["Movie2"].iloc[i]})', fontsize=8)
        for i in range(len(comedy_movies)):
            ax.text(comedy_movies['Movie1'].iloc[i], comedy_movies['Movie2'].iloc[i], f'({comedy_movies["Movie1"].iloc[i]}, {comedy_movies["Movie2"].iloc[i]})', fontsize=8)

        ax.set_xlabel('Movie A Rating')
        ax.set_ylabel('Movie B Rating')
        ax.set_xlim([0, 6])
        ax.set_ylim([0, 6])
        ax.legend()

        canvas = FigureCanvasTkAgg(fig, master=self.plot_frame)
        canvas.draw()
        canvas.get_tk_widget().pack()

    def predict_preference(self):
        df = pd.read_csv(self.filename)
        X = df[['Movie1', 'Movie2']].values  # 从df中获取所有CSV文件中的评分数据
        y = df['Preference'].values  # 从df中获取所有CSV文件中的电影类型选择

        knn = KNeighborsClassifier(n_neighbors=1)  # 创建KNN分类器, 设置K=1
        knn.fit(X, y)  # 训练分类器

        m1_rating = int(self.movie1_rating.get())  # 获取新用户对电影1的评分
        m2_rating = int(self.movie2_rating.get())  # 获取新用户对电影2的评分
        
        new_point = np.array([[m1_rating, m2_rating]])  # 创建用来做推理的新的数据点

        prediction = knn.predict(new_point)  # 用 knn 预推理的数据点 new_point 的电影类型，0表示动作片，1表示喜剧片
        
        self.predict_label.config(text=f"给用户推荐的电影类型: {'动作片' if prediction[0] == 0 else '喜剧片'}")  # 显示预测结果

        self.plot_ratings()  # 调用plot_ratings方法，更新评分数据的图像

        # 获取最近邻的信息
        distances, indices = knn.kneighbors(new_point)  # 获取新的数据点的最近邻的距离矩阵和索引值矩阵
        nearest_neighbor = X[indices[0][0]]  # 获取最近邻的坐标，这是一个列表，第一个元素是x坐标，第二个元素是y坐标

        # 绘制新数据点和连接线
        fig, ax = plt.subplots()
        ax.grid(True)
        
        # 重新绘制所有数据点
        df = pd.read_csv(self.filename)
        action_movies = df[df['Preference'] == 0]
        comedy_movies = df[df['Preference'] == 1]
        ax.scatter(action_movies['Movie1'], action_movies['Movie2'], color='orange', label='Action')
        ax.scatter(comedy_movies['Movie1'], comedy_movies['Movie2'], color='blue', label='Comedy')
        
        # 绘制新数据点
        ax.scatter(new_point[:, 0], new_point[:, 1], color='red', marker='x', s=100, label='New User')
        
        # 绘制连接线
        ax.plot([new_point[0, 0], nearest_neighbor[0]], [new_point[0, 1], nearest_neighbor[1]], 'r--')
        
        # 添加坐标文本
        for i in range(len(action_movies)):
            ax.text(action_movies['Movie1'].iloc[i], action_movies['Movie2'].iloc[i], f'({action_movies["Movie1"].iloc[i]}, {action_movies["Movie2"].iloc[i]})', fontsize=8)
        for i in range(len(comedy_movies)):
            ax.text(comedy_movies['Movie1'].iloc[i], comedy_movies['Movie2'].iloc[i], f'({comedy_movies["Movie1"].iloc[i]}, {comedy_movies["Movie2"].iloc[i]})', fontsize=8)
        
        ax.set_xlabel('Movie A Rating')
        ax.set_ylabel('Movie B Rating')
        ax.set_xlim([0, 6])
        ax.set_ylim([0, 6])
        ax.legend()

        # 更新画布
        self.plot_frame.destroy()
        self.plot_frame = tk.Frame(self.root)
        self.plot_frame.grid(row=0, column=2, rowspan=4, padx=10, pady=10)
        
        canvas = FigureCanvasTkAgg(fig, master=self.plot_frame)
        canvas.draw()
        canvas.get_tk_widget().pack()

root = tk.Tk()
app = MovieRatingApp(root)
root.mainloop()